package com.lefevre.cms.web.action.install;

import com.lefevre.cms.bean.RequestResult;
import com.lefevre.cms.bean.ResultCode;
import com.lefevre.cms.bean.install.Install;
import com.lefevre.cms.service.data.DataService;
import com.lefevre.cms.utils.*;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.properties.PropertyValueEncryptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;

/**
 * 安装系统
 */
@Controller
public class InstallManageAction {
    private static final Logger logger = LoggerFactory.getLogger(InstallManageAction.class);

    @Resource
    InstallManage installManage;
    @Resource
    DataService mySqlDataService;

    @Autowired
    private DataSource dataSource;

    /**
     * 数据库密码
     */
    @Value("${spring.datasource.password}")
    private String password;

    /**
     * 盐值 数据库密码加密所需的salt(盐)
     */
    @Value("${jasypt.encryptor.password:123456}")
    private String salt;

    /**
     * 是否允许显示图形界面安装系统
     */
    @Value("${bbs.allowInstallSystem:true}")
    private boolean allowInstallSystem = true;

    /**
     * 安装 添加
     */
    @RequestMapping(value = "/install", method = RequestMethod.GET)
    public String addUI(ModelMap model) throws Exception {
        if (!allowInstallSystem) {
            return null;
        }
        if (isCreateFolder()) {
            LinkedHashMap<String, Integer> folderInfoMap = installManage.createFolder(installManage.folderList());
            model.addAttribute("folderInfoMap", folderInfoMap);
            Map<String, String> errorMap = installManage.createInstallStatus();
            model.addAttribute("errorMap", errorMap);
        } else {
            List<String> pathList = new ArrayList<>();
            pathList.add("WEB-INF/data/install/status.txt");
            Map<String, String> errorMap = installManage.isFilePermission(pathList);
            for (Map.Entry<String, String> entry : errorMap.entrySet()) {
                model.addAttribute("status", "安装状态文件" + entry.getKey() + " " + entry.getValue());
            }
        }
        if (isInstallSystem()) {
            //WEB-INF/data/install/install.ftl
            return "/install";
        }
        return null;
    }

    /**
     * 安装 添加
     */
    @ResponseBody
    @RequestMapping(value = "/install", method = RequestMethod.POST)
    public String add(Install formbean) throws Exception {
        if (!allowInstallSystem) {
            return null;
        }
        Map<String, String> error = new HashMap<>();
        if (!this.isInstallSystem()) {
            error.put("installSystem", "不允许安装系统");
        } else {
            //检测文件权限
            List<String> pathList = new ArrayList<>();
            //待检测文件
            pathList.add("WEB-INF/data/install/status.txt");
            Map<String, String> errorMap = installManage.isFilePermission(pathList);
            for (Map.Entry<String, String> entry : errorMap.entrySet()) {
                error.put("status", "安装状态文件" + entry.getKey() + " " + entry.getValue());
            }
            if (formbean.getDatabasePassword() == null || "".equals(formbean.getDatabasePassword().trim())) {
                error.put("databasePassword", "数据库密码不能为空");
            }
            if (formbean.getUserAccount() == null || "".equals(formbean.getUserAccount().trim())) {
                error.put("userAccount", "管理员账号不能为空");
            }
            if (formbean.getUserPassword() == null || "".equals(formbean.getUserPassword().trim())) {
                error.put("userPassword", "管理员密码不能为空");
            }
            //校验数据库密码
            if (error.size() == 0) {
                String jdbc_password = password;
                if (salt != null && !"".equals(salt.trim())) {
                    PooledPBEStringEncryptor encryptor = JasyptUtil.config(salt);
                    //加密所需的salt(盐)
                    encryptor.setPassword(salt.trim());
                    //判断是否为加密值 被ENC()包裹
                    if (PropertyValueEncryptionUtils.isEncryptedValue(password)) {
                        jdbc_password = PropertyValueEncryptionUtils.decrypt(password, encryptor);
                    }
                }
                if (!jdbc_password.equals(formbean.getDatabasePassword().trim())) {
                    error.put("databasePassword", "数据库密码错误");
                }
            }
            //校验数据库连接
            if (error.size() == 0) {
                Connection conn = null;
                ResultSet rs = null;
                ResultSet rs2 = null;
                try {
                    conn = dataSource.getConnection();
                    //获取所有表
                    rs = conn.getMetaData().getTables(conn.getCatalog(), "%", "%", new String[]{"TABLE"});
                    List<String> tableNameList = new ArrayList<>();
                    while (rs.next()) {
                        tableNameList.add(rs.getString("TABLE_NAME"));
                    }
                    //通过查询运行设置字符集的命令
                    rs2 = conn.prepareStatement("show variables like 'char%'").executeQuery();
                    String character_set_database = "";
                    while (rs2.next()) {
                        if ("character_set_database".equalsIgnoreCase(rs2.getString(1))) {
                            character_set_database = rs2.getString(2);
                        }
                    }
                    if (!"utf8mb4".equalsIgnoreCase(character_set_database)) {
                        error.put("databaseName", "数据库必须为utf8mb4编码");
                    }
                    int count = 0;
                    //如果数据库表已有数据则不允许安装
                    for (String tableName : tableNameList) {
                        Long c = mySqlDataService.findCount(tableName);
                        if (c > 0L) {
                            count++;
                        }
                    }
                    if (count > 0) {
                        error.put("installSystem", "数据库表已含有数据，不允许安装");
                    }
                } catch (Exception e) {
                    error.put("databaseLink", "数据库连接错误");
                    logger.error("校验数据库连接错误", e);
                } finally {
                    if (rs != null) {
                        try {
                            rs.close();
                        } catch (SQLException e) {
                            error.put("databaseLink", "数据库关闭查询所有表错误");
                        }
                    }
                    if (rs2 != null) {
                        try {
                            rs2.close();
                        } catch (SQLException e) {
                            error.put("databaseLink", "数据库关闭查询字符集编码错误");
                        }
                    }
                    if (conn != null) {
                        try {
                            conn.close();
                        } catch (SQLException e) {
                            error.put("databaseLink", "数据库关闭错误");
                        }
                    }
                }
            }
            if (error.size() == 0) {
                Connection conn = null;
                Statement stmt = null;
                InputStream quartz_inputStream = null;
                InputStream data_inputStream = null;
                try {
                    conn = dataSource.getConnection();
                    //通过查询运行设置字符集的命令
                    conn.prepareStatement("set names utf8mb4").executeQuery();

                    // 导入初始化数据
                    ClassPathResource data_classPathResource = new ClassPathResource("WEB-INF/data/install/data_tables_mysql.sql");
                    data_inputStream = data_classPathResource.getInputStream();
                    //导入quartz SQL文件
                    SqlFile.importSQL(conn, new InputStreamReader(data_inputStream, StandardCharsets.UTF_8));

                    //BCrypt密码算法,Bcrypt加密最长为72个字符，超过72字符的部分被截断丢弃了
                    PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

                    // 密码通过盐值加密以备存储入数据库
                    String newPassword = passwordEncoder.encode(SHA.sha256Hex(formbean.getUserPassword().trim()));
                    //插入管理员数据
                    String sql = "INSERT INTO `sysusers` (`userId`,`enabled`,`fullName`,`issys`,`securityDigest`,`userAccount`,`userDesc`,`userDuty`,`userPassword`) VALUES ('" + UUIDUtil.getUUID32() + "',b'1','" + formbean.getUserAccount().trim() + "',b'1','" + UUIDUtil.getUUID32() + "','" + formbean.getUserAccount().trim() + "',NULL,'管理员','" + newPassword + "')";

                    stmt = conn.createStatement();
                    stmt.executeUpdate(sql);
                    conn.commit();
                } catch (RuntimeException e) {
                    error.put("installSystem", "导入数据库错误");
                } catch (Exception e) {
                    error.put("installSystem", "安装轻论坛系统数据库连接数据库错误");
                    logger.error("安装轻论坛系统数据库连接数据库错误", e);
                } finally {
                    if (data_inputStream != null) {
                        data_inputStream.close();
                    }
                    if (stmt != null) {
                        try {
                            stmt.close();
                        } catch (SQLException e) {
                            logger.error("安装轻论坛系统数据库关闭Statement错误", e);
                        }
                    }
                    if (conn != null) {
                        try {
                            conn.close();
                        } catch (SQLException e) {
                            logger.error("安装轻论坛系统数据库关闭错误", e);
                        }
                    }
                }
            }
            //复制文件
            if (error.size() == 0) {
                FileUtil.copyResourceDirectory("WEB-INF/front/templates", "WEB-INF/front/templates");
                FileUtil.copyResourceDirectory("static/common", "common");
                //写入禁止安装系统
                FileUtil.writeStringToFile("WEB-INF/data/install/status.txt", "1", "utf-8", false);
            }
            if (error.size() == 0) {
                return JsonUtils.toJSONString(new RequestResult(ResultCode.SUCCESS, null));
            }
        }
        return JsonUtils.toJSONString(new RequestResult(ResultCode.FAILURE, error));
    }

    /**
     * 是否允许安装系统
     */
    private boolean isInstallSystem() {
        //读取版本文件
        String version = FileUtil.readFileToString("WEB-INF/data/install/status.txt", "UTF-8");
        return Objects.equals(version, "0");
    }

    /**
     * 是否创建文件夹
     */
    private boolean isCreateFolder() {
        //生成文件目录
        Path path = Paths.get(PathUtil.defaultExternalDirectory() + "/WEB-INF/data/install/status.txt");
        //文件是否存在
        return !Files.exists(path, LinkOption.NOFOLLOW_LINKS);
    }
}

